#define INTERNAL_NS_MALLOC
#include "NewSyndrome.h"

#if defined ENABLE_NS_MALLOC && defined NS_MALLOC_DEV

#define MAX_ALLOC_OP 1000

struct table_file
{
	char *filename;
	module *ptr;
};

char **tab_file = NULL;
static unsigned int nbfile = 0;
static unsigned int nbfree = 0, nballoc = 0, nbrealloc = 0;
static unsigned int nberror = 0, nbwarning = 0;

struct cell
{
	void *ptr;
	size_t size;
	unsigned int line;
	char *source;
};

struct ns_malloc_mod
{
	char *modname;
	module *ref;
	unsigned int nbcell;
	struct cell *tab_malloc;
};

static unsigned int nbmod = 0;
static struct ns_malloc_mod *tab_mod = NULL;

void *ns_calloc (const char *file, unsigned int line, size_t nmemb, size_t size);
void *ns_malloc (const char *file, unsigned int line, size_t size);
void ns_free (const char *file, unsigned int line, void *ptr);
void *ns_realloc (const char *file, unsigned int line, void *ptr, size_t size);
char *ns_strdup (const char *file, unsigned int line, const char *s);


static struct cell *find_free_cell (struct ns_malloc_mod *pmod)
{
	int ind = 0;

	for (ind = 0; ind < pmod->nbcell; ind++)
		if (!pmod->tab_malloc[ind].ptr)
			break;
		
	if (ind == pmod->nbcell)
	{
		pmod->nbcell++;
		pmod->tab_malloc = (struct cell *) realloc (pmod->tab_malloc, sizeof (struct cell) * (pmod->nbcell));
	}
	
	return (&(pmod->tab_malloc[ind]));
}


static struct ns_malloc_mod *find_ns_malloc_mod (void)
{
	int i;
	
	if (ns_core.bot_state != MODULE && nbmod > 0)
		return (&tab_mod[0]);
	
	for (i = 1; i < nbmod; i++)
		if (ns_core.current_module == tab_mod[i].ref)
			return (&tab_mod[i]);
	
	tab_mod = (struct ns_malloc_mod *) realloc (tab_mod, sizeof (struct ns_malloc_mod) * (nbmod + 1));
	if (ns_core.bot_state != MODULE)
	{
		tab_mod[nbmod].modname = strdup ("kernel");
		tab_mod[nbmod].ref = NULL;
	}
	else
	{
		tab_mod[nbmod].modname = strdup (ns_core.current_module->name);
		tab_mod[nbmod].ref = ns_core.current_module;
	}
	tab_mod[nbmod].tab_malloc = NULL;
	tab_mod[nbmod].nbcell = 0;
	
	nbmod++;
	
	return (&tab_mod[nbmod - 1]);
}


static int link_with_tab_file (struct cell *p, const char *file)
{
	int i;
	
	for (i = 0; i < nbfile; i++)
		if (!strcmp (tab_file[i], file))
		{
			p->source = tab_file[i];
			return (1);
		}
		
	tab_file = (char **) realloc (tab_file, sizeof (char *) * (nbfile + 1));
	tab_file[nbfile] = strdup (file);
	p->source = tab_file[nbfile];
	nbfile++;

	return (1);
}


static void fill_malloc_cell (struct cell *p, void *ptr, int size, const char *file, unsigned int line)
{
	p->ptr = ptr;
	p->size = size;
	p->line = line;
	
	link_with_tab_file (p, file);
}


void *ns_malloc (const char *file, unsigned int line, size_t size)
{
	void *ptr;
	struct ns_malloc_mod *pmod;
	struct cell *c;
	
	pmod = find_ns_malloc_mod ();
	c = find_free_cell (pmod);

	if (size == (size_t)0)
	{
		printf ("!!! NS_MALLOC : malloc (%s@%s:%d) size 0 !!!\n", pmod->modname, file, line);
		nbwarning++;
	}
	
	ptr = (void *) malloc (size);
	
	fill_malloc_cell (c, ptr, size, file, line);
	
	if (!ptr)
	{
		printf ("!!! NS_MALLOC : malloc (%s@%s:%d) returns NULL pointer !!!\n", pmod->modname, file, line);
		nberror++;
	}
	
	nballoc++;
	return (ptr);	
}


void *ns_calloc (const char *file, unsigned int line, size_t nmemb, size_t size)
{
	void *ptr;
	struct ns_malloc_mod *pmod;
	struct cell *c;
	
	pmod = find_ns_malloc_mod ();
	c = find_free_cell (pmod);
	
	ptr = (void *) calloc (nmemb, size);
	
	fill_malloc_cell (c, ptr, size * nmemb, file, line);
	
	if (!ptr)
	{
		printf ("!!! NS_MALLOC : calloc (%s@%s:%d) returns NULL pointer !!!\n", pmod->modname, file, line); 
		nberror++;
	}
	
	nballoc++;
	return (ptr);	
}


static struct cell *find_cell_by_ptr (void *ptr)
{
	int i, k;
	
	for (k = 0; k < nbmod; k++)
		for (i = 0; i < tab_mod[k].nbcell; i++)
		{
			if (tab_mod[k].tab_malloc[i].ptr == ptr)
				return (&(tab_mod[k].tab_malloc[i]));
		}
	
	return (NULL);
}


void *ns_realloc (const char *file, unsigned int line, void *ptr, size_t size)
{
	void *rp;
	struct cell *c;
	
	if (!ptr)
		return (ns_malloc (file, line, size));

	if (size == (size_t)0)
	{
		ns_free (file, line, ptr);
		return (NULL);
	}

	c = find_cell_by_ptr (ptr);
		
	if (!c)
	{
		printf ("!!! NS_MALLOC : %s:%d realloc unmalloced ptr !!!\n", file, line);
		nberror++;
	}
	
	/* je ne gere pas l'erreur */
	
	rp = realloc (ptr, size);
	fill_malloc_cell (c, rp, size, file, line);
	
	nbrealloc++;
	return (rp);
}


void ns_free (const char *file, unsigned int line, void *ptr)
{
	struct cell *c;

	if (!ptr)
	{
		printf ("!!! NS_MALLOC : %s:%d free NULL ptr !!!\n", file, line);
		nberror++;
	}
	else
	{
		c = find_cell_by_ptr (ptr);
		if (!c)
		{
			printf ("!!! NS_MALLOC : %s:%d free unmalloced ptr !!!\n", file, line);
			nberror++;
		}
		else
			c->ptr = NULL;

		nbfree++;
		free (ptr);
	}
}


char *ns_strdup (const char *file, unsigned int line, const char *s)
{
	char *ptr;
	struct ns_malloc_mod *pmod;
	struct cell *c;
	
	pmod = find_ns_malloc_mod ();
	c = find_free_cell (pmod);
	
	ptr = strdup (s);
	
	fill_malloc_cell (c, (void *) ptr, strlen (s) * sizeof (char), file, line);
	
	if (!ptr)
	{
		printf ("!!! NS_MALLOC : strdup (%s@%s:%d) returns NULL pointer !!!\n", pmod->modname, file, line); 
		nberror++;
	}
	
	nballoc++;
	return (ptr);	
}


void ns_malloc_show_leak (void)
{
	int i, k;
	unsigned int total, supertotal = 0;
	
	puts ("\n----------------------- NS MALLOC -----------------------");
	if (nberror)
		printf ("Total error : %u errors\n\n", nberror);
	else
		printf ("Aucune erreur\n\n");

	if (nbwarning)
		printf ("Total warning : %u warnings\n\n", nbwarning);
	else
		printf ("Aucun warning\n\n");

	for (i = 0; i < nbmod; i++)
	{
		if (tab_mod[i].nbcell)
		{
			for (k = 0, total = 0; k < tab_mod[i].nbcell; k++)
			{
				if (tab_mod[i].tab_malloc[k].ptr)
				{
					total += tab_mod[i].tab_malloc[k].size;
					printf ("%s %s:%u  %u octets\n", tab_mod[i].modname, tab_mod[i].tab_malloc[k].source, tab_mod[i].tab_malloc[k].line, (unsigned int) tab_mod[i].tab_malloc[k].size);
				}
			}
			supertotal += total;
			if (total)
				printf ("Total %s :    %u octets\n\n", tab_mod[i].modname, total);
		}
	}
	
	if (supertotal)
		printf ("Total leak : %u octets soit %u Kio\n", supertotal, supertotal / 1024);
	else
		printf ("Pas de memory leak\n");
	
	printf ("\n%u allocs / %u frees et %u reallocs\n", nballoc, nbfree, nbrealloc);
	puts ("---------------------------------------------------------\n");
	      
}


void ns_malloc_close (void)
{
	int i;
	for (i = 0; i < nbmod; i++)
	{
		free (tab_mod[i].modname);
		free (tab_mod[i].tab_malloc);
	}
	free (tab_mod);
	tab_mod = NULL;
	nbmod = 0;
	
	for (i = 0; i < nbfile; i++)
		free (tab_file[i]);
	free (tab_file);
	tab_file = NULL;
	nbfile = 0;

	nballoc = 0;
	nbfree = 0;
}

#endif
